////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2001.
// -------------------------------------------------------------------------
//  File name:   entitynode.h
//  Version:     v1.00
//  Created:     23/4/2002 by Timur.
//  Compilers:   Visual C++ 7.0
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#ifndef __entitynode_h__
#define __entitynode_h__

#if _MSC_VER > 1000
//#pragma once
#endif

#include <set>
#include "AnimNode.h"
#include "SoundTrack.h"
#include "StlUtils.h"

#include <IFacialAnimation.h>

#define ENTITY_SOUNDTRACKS	3
#define ENTITY_EXPRTRACKS		3
#define MAX_CHARACTER_TRACKS 3
#define ADDITIVE_LAYERS_OFFSET 6
#define LOOKIK_LAYER 15

// When preloading of a trackview sequence is triggered, the animations in 
// the interval [0 ; ANIMATION_KEY_PRELOAD_INTERVAL] are preloaded into memory.
// When the animation is running Animate() makes sure that all animation keys
// between in the interval [currTime; currTime + ANIMATION_KEY_PRELOAD_INTERVAL]
// are kept in memory.
// This value should be adjusted so that the streaming system has enough
// time to stream the keys in.
#define ANIMATION_KEY_PRELOAD_INTERVAL 2.0f

typedef std::set<string>	TStringSet;
typedef TStringSet::iterator	TStringSetIt;

struct ISkeletonAnim;

class CAnimationCacher
{
public:
	CAnimationCacher() { m_cachedAnims.reserve(MAX_CHARACTER_TRACKS); };
	~CAnimationCacher() { ClearCache(); };
	void AddToCache(uint32 animPathCRC);
	void RemoveFromCache(uint32 animPathCRC);
	void ClearCache();
private:
	std::vector<uint32> m_cachedAnims;
};

class CAnimEntityNode : public CAnimNode
{
public:
	CAnimEntityNode();
	~CAnimEntityNode();
	static void Initialize();

	virtual EAnimNodeType GetType() const { return ANODE_ENTITY; }

	virtual void SetEntityGuid( const EntityGUID &guid );
	virtual EntityGUID* GetEntityGuid() { return &m_entityGuid; };
	virtual IEntity* GetEntity();

	virtual void SetEntityGuidTarget( const EntityGUID &guid );
	virtual void SetEntityGuidSource( const EntityGUID &guid );

	virtual void EnableBoneLinkTransform(bool enable);
	virtual bool IsBoneLinkTransformEnabled() const;

	//////////////////////////////////////////////////////////////////////////
	// Overrides from CAnimNode
	//////////////////////////////////////////////////////////////////////////
	virtual void StillUpdate();
	virtual void Animate( SAnimContext &ec );
	virtual void CreateDefaultTracks();

	void SetPos( float time,const Vec3 &pos );
	void SetRotate( float time,const Quat &quat );
	void SetScale( float time,const Vec3 &scale );

	void PrecacheKeys(float time);

	float GetTime() { return m_time; };
	Vec3 GetPos() { return m_pos; };
	Quat GetRotate() { return m_rotate; };
	Vec3 GetScale() { return m_scale; };

	virtual void Activate( bool bActivate );

	//////////////////////////////////////////////////////////////////////////
	void Serialize( XmlNodeRef &xmlNode,bool bLoading,bool bLoadEmptyTracks );
	void Reset();
	void ResetHard();
	void Pause();

	virtual XmlNodeRef SaveToColladaInFixedFPS(float fps, const ENodeExportType exportType, const IAnimSequence* seq = NULL, const IEntity* ent = NULL)const;
	virtual bool LoadFromCollada(XmlNodeRef xmlNode);

	//////////////////////////////////////////////////////////////////////////
	virtual int GetParamCount() const;
	virtual bool GetParamInfo( int nIndex, SParamInfo &info ) const;
	virtual bool GetParamInfoFromId( int paramId, SParamInfo &info ) const;

	static int GetParamCountStatic();
	static bool GetParamInfoStatic( int nIndex, SParamInfo& info );

	void GetMemoryUsage( ICrySizer *pSizer ) const
	{
		pSizer->AddObject(this, sizeof(*this));
		pSizer->AddObject(m_setAnimationSinks);
		pSizer->AddObject(m_SoundInfo);
		pSizer->AddObject(m_setExpressions);
		pSizer->AddObject(m_lookAtTarget);				
		CAnimNode::GetMemoryUsage(pSizer);
	}

protected:
	void ReleaseSounds();
	void ApplyEventKey( class CEventTrack *track,int keyIndex,IEventKey &key );
	void ApplySoundKey( IAnimTrack *pTrack,int nCurrKey,int nLayer, ISoundKey &key, SAnimContext &ec);
	Vec3 Adjust3DSoundOffset(bool bVoice, IEntity *pEntity, Vec3& oSoundPos) const;
	void AnimateCharacterTrack( class CCharacterTrack* track,SAnimContext &ec,int layer );
	bool CheckTimeJumpingOrOtherChanges( const SAnimContext &ec, int32 activeKeys[], int32 numActiveKeys, ICharacterInstance * pCharacter, int layer );
	void UpdateAnimTimeJumped( int32 keyIndex, class CCharacterTrack* track, float ectime, ICharacterInstance * pCharacter, int layer, bool bAnimEvents );
	void UpdateAnimRegular( int32 numActiveKeys, int32 activeKeys[], class CCharacterTrack* track, float ectime, ICharacterInstance * pCharacter, int layer, bool bAnimEvents );
	void UpdateAnimBlendGap( int32 activeKeys[], class CCharacterTrack* track, float ectime, ICharacterInstance * pCharacter, int layer );
	void ApplyAnimKey( int32 keyIndex, class CCharacterTrack* track, float ectime, 
										ICharacterInstance * pCharacter, int layer, int animIndex, bool bAnimEvents );
	void StopExpressions();
	void AnimateExpressionTrack(class CExprTrack *pTrack, SAnimContext &ec);
	void AnimateFacialSequence(class CFaceSeqTrack *pTrack, SAnimContext &ec);
	void AnimateLookAt(class CLookAtTrack *pTrack, SAnimContext &ec);

	void ReleaseAllAnims();
	virtual void OnStartAnimation(const char *sAnimation) {}
	virtual void OnEndAnimation(const char *sAnimation);

	bool AnimationPlaying() const
	{
		return (m_layerPlaysAnimation[0] || m_layerPlaysAnimation[1] || m_layerPlaysAnimation[2]);
	}

	void PrepareAnimations();

	IFacialAnimSequence* LoadFacialSequence(const char* sequenceName);
	void ReleaseAllFacialSequences();

	void OffsetPosTrack( const Vec3 &offset );

	void UpdateBoneLink( const Vec3 *pPos, const Quat *pRot);
	typedef uint8 TUpdateEntityFlags;
	enum EUpdateEntityFlags
	{
		EUEF_POSITION = 1 << 0,
		EUEF_ROTATION = 1 << 1,
	};

	static void UpdateEntityPositionAndRotation( IEntity* piEntity, const Vec3& targetPos, const Quat& targetRot, const bool initialState, const TUpdateEntityFlags flags );

protected:
	//! Reference to game entity.
	EntityGUID m_entityGuid;
	EntityId m_EntityId;

	TStringSet m_setAnimationSinks;

	CAnimationCacher m_animationCacher;

	//! Pointer to target animation node.
	_smart_ptr<IAnimNode> m_target;

	// Cached parameters of node at given time.
	float m_time;
	Vec3 m_pos;
	Quat m_rotate;
	Vec3 m_scale;

	//! Last animated key in Entity track.
	int m_lastEntityKey;
	int32 m_lastAnimationKeys[3][2];
	bool m_layerPlaysAnimation[3];

	//! This is used to indicate that a time-jumped blending is currently happening in the animation track.
	bool m_bTimeJumped[3];
	float m_jumpTime[3];

	bool m_bWasTransRot;
	bool m_visible;
	//int m_nLastFacialSequenceKey;
	
	std::vector<SSoundInfo> m_SoundInfo;

	TStringSet m_setExpressions;
	
	string m_lookAtTarget;
	EntityId m_lookAtEntityId;
	bool m_lookAtLocalPlayer;
	bool m_allowAdditionalTransforms;
	ELookAtKeyBoneSet m_boneSet;

	typedef std::map<string,_smart_ptr<IFacialAnimSequence>,stl::less_stricmp<string> > FacialSequenceMap;
	FacialSequenceMap m_facialSequences;


	//! Reference LookAt entities.
	EntityGUID m_entityGuidTarget;
	EntityId m_EntityIdTarget;
	EntityGUID m_entityGuidSource;
	EntityId m_EntityIdSource;

	string m_boneLinkName;

	// Pos/rot noise parameters
	struct Noise
	{
		float amp;
		float freq;

		Vec3 Get(float time) const;

		Noise() : amp(0), freq(0) {}
	};
	Noise m_posNoise;
	Noise m_rotNoise;

	void StopAnimationsAllLayers(ISkeletonAnim *pSkeletonAnim);
	void StartAnimation(ISkeletonAnim *pSkeletonAnim, const char *animName, const CryCharAnimationParams& animParams);
	void ManualSeekAnimationInFIFO(ISkeletonAnim *pSkeletonAnim, uint32 layer, uint32 animIndex, float time, bool bAnimEvents);
	void SetLayerTime(ISkeletonAnim *pSkeletonAnim, uint32 layer, float time);
	void SetTrackViewMixingWeight(ISkeletonAnim *pSkeletonAnim, uint32 layer, float weight);
	void SelectLookIKBlends(ELookAtKeyBoneSet boneSet, float* blends);
};

#endif // __entitynode_h__
